package rest.jbpm.services;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import org.jbpm.api.history.HistoryComment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rest.jbpm.JbpmService;
import rest.jbpm.ProcessEngineException;
import rest.jbpm.filters.ClearNamespaceFilter;
import rest.jbpm.filters.UnwrappingFilter;
import rest.jbpm.task.FindCoWorkerslTasksUnwrapped;
import rest.jbpm.task.FindGroupTasksUnwrapped;
import rest.jbpm.task.FindPersonalTasksUnwrapped;
import rest.jbpm.task.FindTaskById;
import rest.jbpm.task.FindTasksByVariableCmd;
import rest.jbpm.task.FindTasksUnwrapped;
import rest.jbpm.wrappers.*;
import rest.jbpm.wrappers.lists.*;

@Path("tasks")
public class TaskService extends JbpmService {

	private static Logger log = LoggerFactory.getLogger(TaskService.class);
	@Context UriInfo uriInfo;
	
	@GET
	@Produces("text/xml")
	public TaskList listTasks() {
		
        log.debug("listing tasks");

        if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

        /**
         * performance trick to force hand-made unwrapping over unwrapping filter
         */

        if(uriInfo.getQueryParameters().containsKey(UnwrappingFilter.UNWRAP))
            uriInfo.getQueryParameters().remove(UnwrappingFilter.UNWRAP);
        
        uriInfo.getQueryParameters().add(ClearNamespaceFilter.CLEANUP, "true");
        
        return new TaskList(processEngine.execute(new FindTasksUnwrapped()),
            uriInfo.getBaseUri().toString(),true);
	}

	
	@GET 
	@Path("{taskId}")
	@Produces("text/xml")
	public Task getTask( @PathParam("taskId") String taskId) {
		
        log.debug("getting task {}", taskId);
        
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

        /*
        org.jbpm.api.task.Task task = processEngine.getTaskService().getTask(taskId);
        if(task != null)
            return new Task(task,uriInfo.getBaseUri().toString());
        else
            throw new WebApplicationException(Response.Status.NOT_FOUND);
         * 
         */
        
        if(uriInfo.getQueryParameters().containsKey(UnwrappingFilter.UNWRAP))
            uriInfo.getQueryParameters().remove(UnwrappingFilter.UNWRAP);
        
        uriInfo.getQueryParameters().add(ClearNamespaceFilter.CLEANUP, "true");
        
        org.jbpm.api.task.Task task = processEngine.execute(new FindTaskById(taskId));
        
        if(task != null)
            return new Task(processEngine.execute(new FindTaskById(taskId)),
                    uriInfo.getBaseUri().toString(), processEngine.getTaskService().getOutcomes(taskId));
        else
            throw new WebApplicationException(Response.Status.NOT_FOUND);
        
	}
	
	@GET 
	@Path("{taskId}/assignee")
	@Produces("text/plain")
	public String getTaskAssignee(@PathParam("taskId") String taskId) {
		
        log.debug("getting task {} asssignee", taskId);
        
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

		return processEngine.getTaskService().getTask(taskId).getAssignee();
	}

	@POST 
	@Path("{taskId}/assign")
	@Consumes("application/x-www-form-urlencoded")
	public void setTaskAssignee(@PathParam("taskId") String taskId,
								@FormParam("assignee") String assignee) {

        assignee = DNC14N.c14n(assignee);
        log.debug("setting task {} asssignee to {}", taskId, assignee);
        
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");
		log.info("taking task " + taskId + " to assigne " + assignee);
        
		processEngine.getTaskService().assignTask(taskId, assignee == null ||
                assignee.isEmpty() ? null : assignee);
	}
	
	@GET
	@Path("personal/{userId}")
	@Produces("text/xml")
	public TaskList getPersonalTasks(@PathParam("userId") String userId) {
		
        log.debug("getting personal tasks for {}", userId);

        userId = DNC14N.c14n(userId);
        
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");
        
        /**
         * performance trick to force hand-made unwrapping over unwrapping filter
         */
        if(uriInfo.getQueryParameters().containsKey(UnwrappingFilter.UNWRAP))
            uriInfo.getQueryParameters().remove(UnwrappingFilter.UNWRAP);
        
        uriInfo.getQueryParameters().add(ClearNamespaceFilter.CLEANUP, "true");

        return new TaskList(processEngine.execute(new FindPersonalTasksUnwrapped(userId)),
            uriInfo.getBaseUri().toString(),true);
		/*
		return new TaskList(processEngine.getTaskService().createTaskQuery().assignee(userId).list(),
				uriInfo.getBaseUri().toString());
         * 
         */
	}
	
	@GET
	@Path("group/{userId}")
	@Produces("text/xml")
	public TaskList getGroupTasks(@Context UriInfo uriInfo,
            @PathParam("userId") String userId) {

        log.debug("getting group tasks for {}", userId);
        
        userId = DNC14N.c14n(userId);
        
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

        /**
         * performance trick to force hand-made unwrapping over unwrapping filter
         */
        if(uriInfo.getQueryParameters().containsKey(UnwrappingFilter.UNWRAP))
            uriInfo.getQueryParameters().remove(UnwrappingFilter.UNWRAP);
        
        uriInfo.getQueryParameters().add(ClearNamespaceFilter.CLEANUP, "true");

        return new TaskList(processEngine.execute(new FindGroupTasksUnwrapped(userId)),
            uriInfo.getBaseUri().toString(),true);
        /*
		return new TaskList(processEngine.getTaskService().findGroupTasks(userId),
				uriInfo.getBaseUri().toString());
         */
	}
	
    @GET
	@Path("coworkers/{groupId}")
	@Produces("text/xml")
	public TaskList getCoWorkersTasks(@Context UriInfo uriInfo,
            @PathParam("groupId") String groupId) {

        log.debug("getting co workers tasks for group {}", groupId);
        
        if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

        /**
        * performance trick to force hand-made unwrapping over unwrapping filter
        */
        if(uriInfo.getQueryParameters().containsKey(UnwrappingFilter.UNWRAP))
            uriInfo.getQueryParameters().remove(UnwrappingFilter.UNWRAP);
        
        uriInfo.getQueryParameters().add(ClearNamespaceFilter.CLEANUP, "true");
        
        return new TaskList(processEngine.execute(new FindCoWorkerslTasksUnwrapped(groupId)),
                uriInfo.getBaseUri().toString());
	}
    
	@GET
	@Path("{taskId}/outcomes")
	@Produces("text/xml")
	public StringList getOutcomes(@PathParam("taskId") String taskId) {
		
        log.debug("getting outcomes for task {}", taskId);
                
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");
		
		return new StringList(processEngine.getTaskService().getOutcomes(taskId));
	}
	
	@GET
	@Path("{taskId}/variableNames")
	@Produces("text/xml")
	public StringList getVariableNames(@PathParam("taskId") String taskId) {

        log.debug("getting variable names for task {}", taskId);
       
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

		return new StringList(processEngine.getTaskService().getVariableNames(taskId));
	}
	
	@GET
	@Path("{taskId}/variable/{variableName}")
	@Produces("text/xml")
	public Variable getVariable(@PathParam("taskId") String taskId, 
            @PathParam("variableName") String variableName) {
        
        log.debug("getting variable {} of task {}", variableName, taskId);
        
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

        Object var = processEngine.getTaskService().getVariable(taskId, variableName);
        if(var != null)
            return new Variable(variableName, var.toString());
        else
            throw new WebApplicationException(Response.Status.NOT_FOUND);
	}
	
	@POST
	@Path("{taskId}/variables")
	@Consumes("application/x-www-form-urlencoded")
	public void setVariables(@PathParam("taskId") String taskId,
            MultivaluedMap<String, String> params) {
		
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");
		
        String execution = processEngine.getTaskService().getTask(taskId).getExecutionId();
        Set<String> vars = processEngine.getExecutionService().getVariableNames(execution);
        
        for(String name : params.keySet()) {
            if(vars.contains(name))
                processEngine.getExecutionService().setVariable(execution, name, params.getFirst(name).toString());
            else
                processEngine.getExecutionService().createVariable(execution, name, params.getFirst(name).toString(), true);
        }
	}
    
    
	@GET
	@Path("{taskId}/comment/{commentId}")
	@Produces("text/xml")
	public Comment getComment(@PathParam("taskId") String taskId,
            @PathParam("commentId") String commentId) {
		
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

		for(HistoryComment hc : processEngine.getTaskService().getTaskComments(taskId)) {
			if(hc.getId().equals(commentId))
				return new Comment(hc);
		}
		throw new WebApplicationException(404);
	}
	
	@PUT
	@Path("{taskId}/comments/add")
	@Consumes("application/x-www-form-urlencoded")
	@Produces("text/xml")
	public Comment addComment(@PathParam("taskId") String taskId,
            @FormParam("comment") String comment) {
	
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

		return new Comment(
				processEngine.getTaskService().addTaskComment(taskId, comment));
	}
	
	@PUT
	@Path("comment/{commentId}/reply")
	@Consumes("application/x-www-form-urlencoded")
	@Produces("text/xml")
	public Comment addReplyComment(@PathParam("commentId") String commentId,
            @FormParam("comment") String comment) {
		
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

		return new Comment(
				processEngine.getTaskService().addReplyComment(commentId, comment));

	}

	@POST
	@Path("{taskId}/end/{outcome}")
	@Consumes("application/x-www-form-urlencoded")
	public void endTask(@PathParam("taskId") String taskId, 
			@PathParam("outcome") String outcome,
            MultivaluedMap<String, String> params) {
		
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");
		
		if(outcome == null) {
			processEngine.getTaskService().completeTask(taskId);
			return;
		}
		
		if(params != null)
			processEngine.getTaskService().completeTask(taskId,outcome,straightenVariables(params));
		else
			processEngine.getTaskService().completeTask(taskId, outcome);

	}
    
    /**
     * Ends task with specified outcome and returns remaining tasks for assignee
     * @param taskId
     *      ending task id
     * @param outcome
     *      task outcome
     * @param outcome
     *      task outcome
     * @param params
     *      
     */
    @POST
	@Path("{taskId}/end/{outcome}/continueFor/{assignee}")
	@Consumes("application/x-www-form-urlencoded")
	public TaskList endTaskAndContinue(@PathParam("taskId") String taskId, 
			@PathParam("outcome") String outcome, @PathParam("assignee") String assignee,
            MultivaluedMap<String, String> params) {
        
        assignee = DNC14N.c14n(assignee);
		
		if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

        /**
         * performance trick to force hand-made unwrapping over unwrapping filter
         */
        Task task = getTask(taskId);
        
        if(uriInfo.getQueryParameters().containsKey(UnwrappingFilter.UNWRAP))
            uriInfo.getQueryParameters().remove(UnwrappingFilter.UNWRAP);
        
        uriInfo.getQueryParameters().add(ClearNamespaceFilter.CLEANUP, "true");
        
        /*return new TaskList(
                processEngine.execute(
                    new FindTasksInProcessUnwrapped(((Execution)task.getExecution()).getId(), assignee)),
                uriInfo.getBaseUri().toString(),true);*/
        return null;
        

	}
	
	private Map<String,Object> straightenVariables(MultivaluedMap<String, String> params) {
        
        HashMap<String, Object> vars = new HashMap<String, Object>();
		for(String key : params.keySet()) {
			int i = 0;
			for(Object value : params.get(key)) {
				vars.put(i == 0 ? key : key + "-" + i, value);
				i++;
			}
		}
		return vars;
	}
    
    @GET
	@Path("variable/{name}/{value}")
	@Produces("text/xml")
    public TaskList getTasksByVariable(@PathParam("name") String name, 
                @PathParam("value") String value) {
        
        if(processEngine == null)
			throw new ProcessEngineException("Fire! fire! mayday! mayday! we're going down!.. JBPM process engine is down!");

        return new TaskList(processEngine.execute(new FindTasksByVariableCmd(name, value)),
                uriInfo.getBaseUri().toString());
        
    }
	
}
